package org.maxgroundstation.groundstation.comm.service;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

import org.maxgroundstation.groundstation.comm.service.alive.ImAliveService;
import org.maxgroundstation.groundstation.comm.service.echo.EchoService;
import org.maxgroundstation.groundstation.comm.service.generic.GenericService;
import org.maxgroundstation.groundstation.comm.service.geometry.GeometryService;
import org.maxgroundstation.groundstation.comm.service.gps.GpsService;
import org.maxgroundstation.groundstation.comm.service.list.ListService;
import org.maxgroundstation.groundstation.comm.service.log.LogService;
import org.maxgroundstation.groundstation.comm.service.video.VideoService;
import org.maxgroundstation.groundstation.comm.service.wifi.WiFiService;

/**
 * A Utility class for service communication.
 * @author Scott Davis
 * 
 * Copyright (C) 2010  Scott Davis
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public final class ServiceUtil {

	/** 
	 * Private constructor for utility class.
	 */
	private ServiceUtil() {};
	
	/**
	 * Parse the data fragment from an array of data.
	 * @param data the array of data.
	 * @return data fragment.
	 */
	static final ServiceDataFragment parseDataFragment(byte[] data) {
		ServiceDataFragment dataFrag = new ServiceDataFragment();
		
		byte[] headerArray = Arrays.copyOfRange(data, 0, 40);
		dataFrag.setHeader(parseDataFragmentHeader(headerArray));
		
		byte[] dataArray = Arrays.copyOfRange(data, 40, data.length);
		dataFrag.setData(dataArray);
		
		return dataFrag;
	}
	
	/**
	 * Parses a data fragment header from an array of bytes.
	 * @param data the array of data.
	 * @return data fragment header.
	 */
	static final ServiceDataFragmentHeader parseDataFragmentHeader(byte[] data) {
		ServiceDataFragmentHeader header = new ServiceDataFragmentHeader();
		
		ByteBuffer buff = ByteBuffer.wrap(data);
		buff.order(ByteOrder.LITTLE_ENDIAN);
		
		long syncHeader = buff.getLong();
		header.setSyncHeader(syncHeader);
		
		StringBuffer serviceIDBuff = new StringBuffer();
		for (int x = 0; x < 8; x++) serviceIDBuff.append((char) buff.get());
		String serviceID = serviceIDBuff.toString();
		header.setServiceID(serviceID);
		
		StringBuffer clientIDBuff = new StringBuffer();
		for (int x = 0; x < 8; x++) clientIDBuff.append((char) buff.get());
		String clientID = clientIDBuff.toString();
		header.setClientID(clientID);
		
		long frameLength = buff.getInt() | 0L;
		header.setFrameLength(frameLength);
		
		int fragSize = buff.getShort() | 0;
		header.setFragSize(fragSize);
		
		int fragNumber = buff.getShort() | 0;
		header.setFragNumber(fragNumber);
		
		long timestamp = buff.getLong();
		header.setTimestamp(timestamp);
		
		return header;
	}
	
	/**
	 * Creates a data fragment.
	 * @param serviceID the service ID.
	 * @param clientID the client ID.
	 * @param frameLength the frame length (bytes).
	 * @param fragNum the fragment number.
	 * @param data the data array.
	 * @return a service data fragment.
	 */
	static final ServiceDataFragment createDataFragment(String serviceID, 
			String clientID, long frameLength, int fragNum, byte[] data) {
		
		ServiceDataFragment dataFrag = new ServiceDataFragment();
		
		ByteBuffer syncBuff = ByteBuffer.allocate(8);
		syncBuff.order(ByteOrder.LITTLE_ENDIAN);
		for (int x = 0; x < 8; x++) syncBuff.put((byte) 0xFF);
		dataFrag.getHeader().setSyncHeader(syncBuff.getLong(0));
		
		dataFrag.getHeader().setServiceID(serviceID);
		
		dataFrag.getHeader().setClientID(clientID);
		
		dataFrag.getHeader().setFrameLength(frameLength);
		
		dataFrag.getHeader().setFragSize(data.length);
		
		dataFrag.getHeader().setFragNumber(fragNum);
		
		dataFrag.getHeader().setTimestamp(System.currentTimeMillis() * 1000L);
		
		dataFrag.setData(data);
		
		return dataFrag;
	}
	
	/**
	 * Gets the service ID from a base service ID with proceeding bytes.
	 * @param baseServiceID the base service ID.
	 * @return the service ID.
	 */
	public static final String getServiceID(String baseServiceID) {
		StringBuffer buff = new StringBuffer(baseServiceID);
		if (baseServiceID.length() < 8) {
			buff.append(new String(new byte[8 - baseServiceID.length()]));
		}
		return buff.toString();
	}
	
	/**
	 * Creates a service based on a service ID.
	 * @param serviceID the service ID.
	 * @param comm the service communication.
	 * @return service.
	 */
	public static final Service createService(String serviceID, ServiceCommunication comm) {
		Service result = null;
		
		if (serviceID == null) throw new IllegalArgumentException("serviceID is null");
		else if (serviceID.trim().isEmpty()) throw new IllegalArgumentException("serviceID is empty");
		
		// Check all known services.
		if (serviceID.equals(ListService.SERVICE_ID.trim())) result = new ListService(comm);
		else if (serviceID.equals(EchoService.SERVICE_ID.trim())) result = new EchoService(comm);
		// Note: The I'm Alive service is no longer used by the MAX kernel.
		//else if (serviceID.equals(ImAliveService.SERVICE_ID.trim())) result = new ImAliveService(comm);
		else if (serviceID.equals(LogService.SERVICE_ID.trim())) result = new LogService(comm);
		else if (serviceID.indexOf(VideoService.SERVICE_BASE_ID.trim()) > -1) result = 
			new VideoService(comm, serviceID);
		else if (serviceID.equals(GeometryService.SERVICE_ID.trim())) result = new GeometryService(comm);
		else if (serviceID.equals(WiFiService.SERVICE_ID.trim())) result = new WiFiService(comm);
		else if (serviceID.equals(GpsService.SERVICE_ID.trim())) result = new GpsService(comm);
		
		if (result == null) {
			// Create generic service.
			System.out.println("Unrecognized service: '" + serviceID + "', creating generic service");
			result = new GenericService(comm, serviceID);
		}
		else System.out.println("Recognized service: '" + serviceID + "'");
		
		return result;
	}
}